/*
	Filename	:	CardSet.cpp
	Author(s)	:	Ray E. Bornert II
	Date		:	2002-APR-22
	Copyright	:	(c) 2002 RealTimeGaming

	History		:
*/

//////////////
// INCLUDES //
//////////////
#include <string.h>
#include <search.h>
#include <stdlib.h>
#include "bjrules.h"

int CardCompare( const void* a, const void* b )
{
	unsigned char *pA = ((unsigned char*)a);
	unsigned char *pB = ((unsigned char*)b);

	//rank first
	if (pA[0] != pB[0])
		return (pA[0] - pB[0]);

	//suit next
	return (pA[1] - pB[1]);	
}

int IsRankInSet
(
	unsigned char rank,
	unsigned char srank
)
{
	int match=0;

	switch(srank)
	{
	case 'V'	:
	case '='	:
	case '*'	:	match = 1;								break;
	case 'a'	:	
	case 'A'	:	match = ((rank == 1) || (rank == 14));	break;
	case 't'	:
	case 'T'	:	match = (rank == 10);					break;
	case 'j'	:
	case 'J'	:	match = (rank == 11);					break;
	case 'q'	:
	case 'Q'	:	match = (rank == 12);					break;
	case 'k'	:
	case 'K'	:	match = (rank == 13);					break;
	case 'x'	:
	case 'X'	:	match = ((10 <= rank) && (rank <= 13));	break;
	case 'f'	:
	case 'F'	:	match = ((11 <= rank) && (rank <= 13));	break;
	case 'l'	:
	case 'L'	:	match = (( 2 <= rank) && (rank <=  9)); break;

	default		:	match = ((rank+'0') == srank);			break;
	}

	//lower case means NOT
	if (('a' <= srank) && (srank <= 'z'))
		match = !match;

	return match;
}

int IsSuitInSet
(
	unsigned char suit,
	unsigned char ssuit
)
{
	int match=0;

	switch(ssuit)
	{
	case '='	:
	case '*'	:	match = 1;								break;
	case 'c'	:
	case 'C'	:	match = (suit == 1);					break;
	case 'd'	:
	case 'D'	:	match = (suit == 2);					break;
	case 'h'	:
	case 'H'	:	match = (suit == 3);					break;
	case 's'	:
	case 'S'	:	match = (suit == 4);					break;
	case 'b'	:
	case 'B'	:	match = ((suit == 1) || (suit == 4));	break;
	case 'r'	:
	case 'R'	:	match = ((suit == 2) || (suit == 3));	break;

	default		:	match = 0;								break;
	}

	//lower case means NOT
	if (('a' <= ssuit) && (suit <= 'z'))
		match = !match;

	return match;
}

int IsCardInSet
(
	unsigned char rank,
	unsigned char suit,
	unsigned char srank,
	unsigned char ssuit
)
{
	if (!IsRankInSet( rank, srank ))
		return 0;

	if (!IsSuitInSet( suit, ssuit ))
		return 0;

	return 1;
}

int IsCardsInSet( unsigned char *pCards, int nCards, unsigned char *pSet )
{
	//how many cards in the set definition
	int setlen = strlen((const char*)pSet);
	if (setlen != nCards*2)
		return 0;

	//gotta have at least 1 card
	if (setlen < 2)
		return 0;

	//we need a new card array for working storage
	unsigned char _pCards[256];

	//can't have more cards than will fit our working storage
	if (setlen > sizeof(_pCards))
		return 0;

	//move the cards into the working array
	for (int i=0; i<nCards; i++)
	{
		//get the source card byte
		unsigned char c = pCards[i];

		//suit is hi order nibble
		unsigned char suit = (c >> 4) & 0x0f;

		//rank is lo order nibble
		unsigned char rank = (c >> 0) & 0x0f;

		//dest size is 2 bytes
		int k=i*2;
		_pCards[k+0] = rank;
		_pCards[k+1] = suit;
	}

	//sort the cards in ascending order
	qsort( _pCards, nCards, 2, CardCompare );

	//now we must preprocess the set string for '=' char's
	unsigned char samerank=0;
	unsigned char samesuit=0;
	unsigned char samevalue=0;

	//cards with an '=' in the rule rank placeholder must match each other
	//cards with an '=' in the rule suit placeholder must match each other
	// "6=7=" would match any 6 and 7 of the same suit
	// "=d=s" would match any diamond and spade of the same rank
	for (i=0; i<nCards; i++)
	{
		int k=i*2;

		//rank
		if (pSet[k+0] == 'V')
		if (!samevalue)
			samevalue  = BJRules::CardRankValue(_pCards[k+0]);
		else
		if (samevalue != BJRules::CardRankValue(_pCards[k+0]))
			return 0;

		//rank
		if (pSet[k+0] == '=')
		if (!samerank)
			samerank  = _pCards[k+0];
		else
		if (samerank != _pCards[k+0])
			return 0;

		//suit
		if (pSet[k+1] == '=')
		if (!samesuit)
			samesuit  = _pCards[k+1];
		else
		if (samesuit != _pCards[k+1])
			return 0;
	}

	//ok do the match testing
	for (i=0; i<nCards; i++)
	{
		int k=i*2;
		if (!IsCardInSet( _pCards[k], _pCards[k+1], pSet[k], pSet[k+1] ))
			return 0;
	}

	//match
	return 1;
}

int IsCardsOutsideSet( unsigned char *pCards, int nCards, unsigned char *pSet )
{
	if (pCards == NULL)
		return 0;

	//how many cards in the set definition
	int setlen = strlen((const char*)pSet);

	//we need a new card array for working storage
	unsigned char _pCards[256];

	//can't have more cards than will fit our working storage
	if (setlen > sizeof(_pCards))
		return 1;

	//move the cards into the working array
	for (int i=0; i<nCards; i++)
	{
		//get the source card byte
		unsigned char c = pCards[i];

		//suit is hi order nibble
		unsigned char suit = (c >> 4) & 0x0f;

		//rank is lo order nibble
		unsigned char rank = (c >> 0) & 0x0f;

		//dest size is 2 bytes
		int k=i*2;
		_pCards[k+0] = rank;
		_pCards[k+1] = suit;
	}

	int	usedList=0;
	int unmatched=nCards;
	for (i=0; i<nCards; i++)
	{
		int k=i*2;
		for (int s=0; s<setlen; s+=2)
		{
			if (usedList & (1<<s))
				continue;

			if (IsCardInSet( _pCards[k], _pCards[k+1], pSet[s], pSet[s+1] ))
			{
				usedList |= (1<<s);
				unmatched--;
				break;
			}
		}
	}

	//a non-zero value means that there were cards outside the set pattern
	return unmatched;
}

unsigned char GetCardFromPattern( char srank, char ssuit )
{
	unsigned char rank;
	unsigned char suit;

	switch(srank)
	{
	case '*'	:	rank = 2+(rand()%13);					break;
	case 'a'	:
	case 'A'	:	rank = 14;								break;
	case 't'	:
	case 'T'	:	rank = 10;								break;
	case 'j'	:
	case 'J'	:	rank = 11;								break;
	case 'q'	:
	case 'Q'	:	rank = 12;								break;
	case 'k'	:
	case 'K'	:	rank = 13;								break;
	case 'x'	:
	case 'X'	:	rank = 10+(rand()%4);					break;
	case 'f'	:
	case 'F'	:	rank = 11+(rand()%3);					break;
	case 'l'	:
	case 'L'	:	rank = 2+(rand()%8);					break;

	default		:	rank = (srank-'0');						break;
	}

	switch(ssuit)
	{
	case '='	:
	case '*'	:	suit = 1+(rand()%4);					break;
	case 'c'	:
	case 'C'	:	suit = 1;								break;
	case 'd'	:
	case 'D'	:	suit = 2;								break;
	case 'h'	:
	case 'H'	:	suit = 3;								break;
	case 's'	:
	case 'S'	:	suit = 4;								break;
	case 'b'	:
	case 'B'	:	suit = (1 << ((rand()%2)*2));			break;
	case 'r'	:
	case 'R'	:	suit = (2 + (rand()%2));				break;

	default		:	suit = 0;								break;
	}

	unsigned char card = (suit << 4) | rank;

	return card;
}
